//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2006-2007
//
// Dialog box base class.
//

# include "StdAfx.h"
# include "Dialog.h"
# include "LinkDlg.h"
# include "Globals.h"

INT_PTR CDialogBase::OnMessage (UINT _uMsg, WPARAM _wParam, LPARAM _lParam) {
	if (m_bHandleURLs) {
		const INT_PTR r = LinkDlgProc (m_hWnd, _uMsg, _wParam, _lParam);
		if (r != 0)
			return r;
	}

	switch (_uMsg) {
		case WM_PAINT: {
			PAINTSTRUCT ps;
			::BeginPaint (m_hWnd, & ps);

			OnPaint (ps);

			::EndPaint (m_hWnd, & ps);
			return 0;
		}

		case WM_CLOSE: {
			OnClose ();
			return 0;
		}

		case WM_DESTROY: {
			OnDestroy ();
			return 0;
		}

		case WM_COMMAND: {
			const WORD wCmd        = LOWORD (_wParam);
			const WORD wNotifyCode = HIWORD (_wParam);
			const HWND hwndCtl     = reinterpret_cast<HWND> (_lParam);

			OnCommand (wCmd, wNotifyCode, hwndCtl);
			return 0;
		}

		case WM_NOTIFY: {
			const int idCtrl = static_cast<int> (_wParam);
			NMHDR * const pInfo = reinterpret_cast<NMHDR *> (_lParam);

			return OnNotify (idCtrl, pInfo);
		}

		case WM_SIZE: {
			const DWORD dwResizeFlag = _wParam;
			const WORD wWidth        = LOWORD (_lParam);
			const WORD wHeight       = HIWORD (_lParam);

			OnSize (dwResizeFlag, wWidth, wHeight);
			return 0;
		}

		case WM_MOVE: {
			const int x = (short) LOWORD (_lParam);
			const int y = (short) HIWORD (_lParam);
			OnMove (x, y);
			return 0;
		}

		case WM_HSCROLL: {
			const WORD wRequest = LOWORD (_wParam);
			const WORD wPos     = HIWORD (_wParam);
			const HWND hwndCtl  = reinterpret_cast<HWND> (_lParam);

			OnHScroll (wRequest, wPos, hwndCtl);
			return 0;
		}

		case WM_VSCROLL: {
			const WORD wRequest = LOWORD (_wParam);
			const WORD wPos     = HIWORD (_wParam);
			const HWND hwndCtl  = reinterpret_cast<HWND> (_lParam);

			OnVScroll (wRequest, wPos, hwndCtl);
			return 0;
		}
	}

	return 0;
}

HWND CDialogBase::GetDlgItem (UINT _uiID) const {
	return ::GetDlgItem (m_hWnd, _uiID);
}

void CDialogBase::GetDlgItemRect (UINT _uiID, RECT & _rect) const {
	const HWND hwndItem = GetDlgItem (_uiID);
	::GetClientRect (hwndItem, & _rect);
	::MapWindowRect (hwndItem, m_hWnd, & _rect);
}

void CDialogBase::SetFocus (UINT _uiID) {
	::SetFocus (GetDlgItem (_uiID));
}

void CDialogBase::EnableDlgItem (UINT _uiID, bool _bEnable) {
	::EnableWindow (GetDlgItem (_uiID), _bEnable ? TRUE : FALSE);
}

void CDialogBase::ShowDlgItem (UINT _uiID, bool _bShow) {
	::ShowWindow (GetDlgItem (_uiID), _bShow ? SW_SHOW : SW_HIDE);
}

void CDialogBase::GetDlgItemText (UINT _uiID, TCHAR * _strText, size_t _cSize) const {
	::GetDlgItemText (m_hWnd, _uiID, _strText, _cSize);
}

void CDialogBase::GetDlgItemText (UINT _uiID, string_t & _str) const {
	const size_t cLen = ::GetWindowTextLength (::GetDlgItem (m_hWnd, _uiID));
	if (cLen == 0) {
		_str.clear ();
		return;
	}

	TCHAR * const pBuf = (TCHAR *) alloca ((cLen + 1)*sizeof (TCHAR));
	::GetDlgItemText (m_hWnd, _uiID, pBuf, cLen + 1);

	_str.assign (pBuf, cLen);
}

size_t CDialogBase::GetDlgItemTextLen (UINT _uiID) const {
	return ::GetWindowTextLength (::GetDlgItem (m_hWnd, _uiID));
}

void CDialogBase::SetDlgItemText (UINT _uiID, const TCHAR * _strText) {
	::SetDlgItemText (m_hWnd, _uiID, _strText ? _strText : "");
}

void CDialogBase::SetDlgItemTextF (UINT _uiID, const TCHAR * _strFormat, ...) {
	va_list vl;
	va_start (vl, _strFormat);

	TCHAR strBuf [MAX_PATH*2];
	::_vsntprintf (strBuf, sizeof (strBuf), _strFormat, vl);

	::SetDlgItemText (m_hWnd, _uiID, strBuf);

	va_end (vl);
}

void CDialogBase::SetDlgItemUInt (UINT _uiID, UINT _uiValue) {
	::SetDlgItemInt (m_hWnd, _uiID, _uiValue, FALSE);
}

UINT CDialogBase::GetDlgItemUInt (UINT _uiID) const {
	return ::GetDlgItemInt (m_hWnd, _uiID, NULL, FALSE);
}

void CDialogBase::CheckDlgButton (UINT _uiID, bool _bCheck) {
	::CheckDlgButton (m_hWnd, _uiID, _bCheck ? BST_CHECKED : BST_UNCHECKED);
}

void CDialogBase::CheckRadioButton (UINT _uiID, const UINT * _pButtons, size_t _cButtons) {
	::CheckRadioButton (m_hWnd, _uiID, _uiID, _uiID);
	// TODO: uncheck all other radio buttons.
}

bool CDialogBase::IsDlgButtonChecked (UINT _uiID) const {
	return ::IsDlgButtonChecked (m_hWnd, _uiID) == BST_CHECKED;
}

void CDialogBase::SetMsgResult (LONG _lMsgResult) {
	::SetWindowLong (m_hWnd, DWL_MSGRESULT, _lMsgResult);
}

void CDialogBase::SendOK () {
	SendCommand (IDOK);
}

void CDialogBase::SendCommand (WPARAM _wParam) {
	::SendMessage (m_hWnd, WM_COMMAND, _wParam, 0);
}

void CDialogBase::PostCommand (WPARAM _wParam) {
	::PostMessage (m_hWnd, WM_COMMAND, _wParam, 0);
}

///////////////////////////////////////////////////////////////////

std::vector<HWND> CDialog::s_vecModelessDlgs;

bool CDialog::IsDialogMessage (MSG * _pMsg) {
	const size_t cDlgs = CDialog::s_vecModelessDlgs.size ();
	for (size_t c = 0; c < cDlgs; ++ c) {
		const HWND hDlg = CDialog::s_vecModelessDlgs [c];
		if (::IsWindow (hDlg) && ::IsDialogMessage (hDlg, _pMsg))
			return true;
	}

	return false;
}

void CDialog::_AddModelessToList () {
	const size_t cDlgs = CDialog::s_vecModelessDlgs.size ();
	for (size_t c = 0; c < cDlgs; ++ c)
		if (CDialog::s_vecModelessDlgs [c] == m_hWnd)
			return;

	CDialog::s_vecModelessDlgs.push_back (m_hWnd);
}

void CDialog::_RemoveModelessFromList () {
	const size_t cDlgs = CDialog::s_vecModelessDlgs.size ();
	for (size_t c = 0; c < cDlgs; ++ c)
		if (CDialog::s_vecModelessDlgs [c] == m_hWnd) {
			CDialog::s_vecModelessDlgs.erase (CDialog::s_vecModelessDlgs.begin () + c);
			return;
		}
}

CDialog::CDialog (WORD _wResID, bool _bHandleURLs) :
	CDialogBase (_wResID, _bHandleURLs)
{}

CDialog::CDialog (WORD _wResID) :
	CDialogBase (_wResID, false)
{}

CDialog::~CDialog () {
	_RemoveModelessFromList ();
}

INT_PTR CALLBACK CDialog::_DlgProc (HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam) {
	switch (_uMsg) {
		case WM_INITDIALOG: {
			::SetWindowLong (_hWnd, GWL_USERDATA, _lParam);
			CDialog * const pThis = reinterpret_cast<CDialog *> (_lParam);
			pThis->m_hWnd = _hWnd;

			const HWND hwndFocus = reinterpret_cast<HWND> (_wParam);
			const BOOL bRet = pThis->OnInit (hwndFocus) ? TRUE : FALSE;

			return bRet;
		}

		default: {
			CDialogBase * const pThis = reinterpret_cast<CDialogBase *> (::GetWindowLong (_hWnd, GWL_USERDATA));
			if (pThis == NULL)
				// NOTE: some messages come before WM_INITDIALOG, so they could not be dispatched using any virtual function.
				return 0;

			pThis->m_hWnd = _hWnd;
			const INT_PTR r = pThis->OnMessage (_uMsg, _wParam, _lParam);

			if (_uMsg == WM_DESTROY)
				// NOTE: avoid to process any messages after WM_DESTROY since the object may be deleted.
				::SetWindowLong (_hWnd, GWL_USERDATA, 0);

			return r;
		}
	}
}

void CDialog::OnCommand (WORD _wCmd, WORD _wNotifyCode, HWND _hwndCtl) {
	if (_wCmd == IDOK)
		EndDialog (1);
	else if (_wCmd == IDCANCEL)
		EndDialog (0);
}

INT_PTR CDialog::ShowModal (HWND _hWndParent) {
	return DlgBox (m_wResID, _hWndParent, _DlgProc, reinterpret_cast<long> (this));
}

void CDialog::EndDialog (INT_PTR _nResult) {
	::EndDialog (m_hWnd, _nResult);
}

bool CDialog::CreateModeless (HWND _hWndParent) {
	return CreateDlg (m_wResID, _hWndParent, _DlgProc, reinterpret_cast<long> (this)) != NULL;
}

void CDialog::Show () {
	_AddModelessToList ();
	::ShowWindow (m_hWnd, SW_SHOW);
}

void CDialog::Hide () {
	_RemoveModelessFromList ();
	::ShowWindow (m_hWnd, SW_HIDE);
}
